#!/usr/bin/python
from ctypes import c_short,c_uint,c_char_p,byref,create_string_buffer,c_long,c_bool
############################
#Import Curses constants
############################
from const import *
#############################
# Internal constants
#############################
EXTERNAL_WINDOW=True
ACCESS_VIOLATION='access violation'
LINES=None
COLS=None

import sys
if sys.platform=='win32':
    if EXTERNAL_WINDOW:
        from _win_pdcurses_w import curses
    else:
        from _win_pdcurses_c import curses


__author__="David Li"
__date__ ="$Oct 2, 2009 4:52:30 PM$"


stdscr=None

class error(Exception):
    def __init__(self,message,cerror):
        self.value=message
        self.cerror=cerror
    def __str__(self):
        return repr(self.value)
        
class Window:
    def __init__(self,height=0,width=0,begin_y=0,begin_x=0,wstruct=None,auto_refresh=False):
        if wstruct:
            self.wstruct=wstruct
        else:
            self.wstruct=newwin(height,width,begin_y,begin_x)
        self._auto_refresh=auto_refresh
    def auto_refresh(self,enable=None):
        if enable is None:
            return self._auto_refresh
        self._auto_refresh=enable
    def keypad(self,yes):
        if yes:
            return curses.keypad(self.wstruct,1)
        else:
            return curses.keypad(self.wstruct,0)
    def addch(self,*args):
        '''
        Print a character.
        Args can be:
            int,int,char
            char
            int,int,char,opts
            char,opts
        where int,int is the y,x coordinates, char is a 1-character string, and
        opts is a list of formatting options.
        '''
        args=list(args)
        if len(args)==1:#just a char,phew
            try:
                args[0]=ord(args[0])
            except TypeError:
                pass #it's already a number
            r=waddch(self.wstruct,args[0])
            if self.auto_refresh(): self.refresh()
            return r
        elif len(args)>=2:#everything else
            if len(args)==3 and type(args[0])==type(0):#int,int,char
                if type(args[2])!=type(0):
                    args[2]=ord(args[2])
                r=mvwaddch(self.wstruct,args[0],args[1],args[2])
                if self.auto_refresh(): self.refresh()
                return r
            elif len(args)>=4:#int,int,char,opts or char,opts
                if type(args[0]==type(0) and type(args[1]==type(0))):#int,int,char,opts
                    if type(args[0])!=type(0):
                        args[0]=ord(args[0])
                else:#char,opts
                    if type(args[0])!=type(0):
                        char=ord(args[0])
                    for opt in args[1:]:#for everything except the character:
                        char=char | opt
                    r=waddch(self.wstruct,char)
                    if self.auto_refresh(): self.refresh()
                    return r
            else:#char,opts
                char=ord(args[0])
                for opt in args[1:]:#for everything except the character:
                    char=char | opt
                r=waddch(self.wstruct,char)
                if self.auto_refresh(): self.refresh()
                return r
    def write(self,st):#for print >> stdscr
        printw(st,pymode=True)
    def getmaxyx(self):
        return (self.getmaxy(),self.getmaxx())
    def getmaxy(self):
        return curses.getmaxy(self.wstruct)
    def getmaxx(self):
        return curses.getmaxx(self.wstruct)
    def refresh(self):
        return wrefresh(self.wstruct)
    def box(self,verch,horch):
        return box(self.wstruct,verch,horch)
    def border(
            self,
            ls=ACS_VLINE,
            rs=ACS_VLINE,
            ts=ACS_VLINE,
            bs=ACS_VLINE,
            tl=ACS_ULCORNER,
            tr=ACS_URCORNER,
            bl=ACS_LLCORNER,
            br=ACS_LRCORNER
        ):
        return wborder(self.wstruct,ls,rs,ts,bs,tl,tr,bl,br)
    def addstr(self,*args):
        '''
        This is gonna be hard.
        Args can be:
            char
            int,int,char
            char,opts
            int,int,char,opts
        int,int is a position in the format y,x.
        If present, will call move, then call addch() for each char in the str,
        combined with opts if present.
        Phew!
        Doesn't actually use the curses addstr functions.
        '''
        is_auto=False
        if self.auto_refresh():
            is_auto=True
            self.auto_refresh(False)
        if len(args)==1:#str
            for ch in args[0]:
                r=self.addch(ch)
                if r==ERR:
                    return r
        else:
            if type(args[0])==type(0):#int,int,str or int,int,str,opts
                if len(args)==3:#int,int,str
                    self.move(args[0],args[1])
                    for ch in args[2]:
                        r=self.addch(ch)
                        if r==ERR:
                            if is_auto:self.auto_refresh(True)
                            return r
                else:#int,int,str,opts
                    chars=[]
                    self.move(args[0],args[1])
                    for ch in args[2]:
                        chars.append(ord(ch))
                    for x,c in enumerate(chars):
                        for opt in args[3:]:
                            chars[x]=chars[x] | opt
                    for ch in chars:
                        if self.addch(ch)==ERR:
                            if is_auto:self.auto_refresh(True)
                            return ERR
            else:#str,opts
                chars=[]
                for c in list(args[0]):
                    chars.append(ord(c))
                for x,c in enumerate(chars):
                    for opt in args[1:]:
                        chars[x]=chars[x] | opt
                for ch in chars:
                    if self.addch(ch)==ERR:
                        if is_auto:self.auto_refresh(True)
                        return ERR
        if is_auto:
            self.auto_refresh(True)
            self.refresh()
        return OK
    def move(self,y,x):
        return wmove(self.wstruct,y,x)
    def clear(self):
        return wclear(self.wstruct)
    def getch(self,y=None,x=None):
        if not x and not y:
            return wgetch(self.wstruct)
        else:
            return mvwgetch(y,x,self.wstruct)
    def getstr(self,y=None,x=None,n=None):
        if n:
            if not y and not x:
                return wgetnstr(self.wstruct,n)
            else:
                self.move(y,x)
                return wgetnstr(self.wstruct,y,x,n)
        else:
            if not y and not x:
                return wgetstr(self.wstruct)
            else:
                self.move(y,x)
                return wgetstr(self.wstruct)

class Pad:
    def __init__(self,nlines,ncols):
        self.pstruct=curses.newpad(nlines,ncols)

def wmove(wstruct,y,x):
    return curses.wmove(wstruct,y,x)

def move(y,x):
    return curses.move(y,x)

def waddch(wstruct,char):
    try:
        char=ord(char)
    except TypeError:
        pass #it's already a number
    return curses.waddch(wstruct,c_long(char))
    
def waddstr(wstruct,strg):
    pass

def mvwaddch(wstruct,y,x,char):
    '''
    The curses mvwaddch function.
    '''
    if type(char)==type(0):#if the char is a number
        return curses.mvwaddch(wstruct,y,x,c_long(char))
    else:
        return curses.mvwaddch(wstruct,y,x,c_long(ord(char)))

def mvwaddstr(wstruct,y,x,strg):
    pass

def wgetch(wstruct):
    return curses.wgetch(wstruct)
    
def mvwgetch(y,x,wstruct):
    return curses.mvwgetch(y,x,wstruct)
    
def wgetstr(wstruct):
    '''
    The curses wgetstr function.
    Note that this doesn't like long strings.
    THIS FUNCTION CANNOT HANDLE INPUT LONGER THAN 255 CHARACTERS.
    TRY TO USE WGETNSTR INSTEAD.
    Parameters:
        wstruct: The window handle.
    '''
    s=create_string_buffer(255)
    curses.wgetstr(wstruct,byref(s))
    return s.value

def wgetnstr(wstruct,n):
    s=c_char_p('')
    curses.wgetnstr(wstruct,byref(n))
    return s.value

def beep():
    '''
    This isn't the curses beep function, which does nothing on my machine.
    It simply prints \\a.
    '''
    print '\a'

def initscr(hook=False):
    '''
    Initializes the curses window. It calls curses2.initscr, then assigns the
    result to wincurses.stdscr.

    Returns a handle to stdscr.
    '''
    try:
        global stdscr
        stdscr=Window(wstruct=curses.initscr())
        LINES,COLS=stdscr.getmaxyx()
        if hook:
            def func(type,value,tb):
                import traceback
                traceback.print_exception(type,value,tb)
                if stdscr:
                    endwin()
            sys.excepthook=func
        return stdscr
    except Exception,e:
        raise error('Unknown error.',e)

def newwin(height,width,begin_y,begin_x):
    return curses.newwin(height,width,begin_y,begin_x)

def endwin():
    try:
        return curses.endwin()
    except Exception,e:
        if ACCESS_VIOLATION in e.message:
            raise error('Curses was not initialized.',e)
        else:
            raise error('Unknown error.',e)

def getch():
    if not stdscr is None:
        return curses.wgetch(stdscr.wstruct)

def refresh():
    return curses.refresh()

def printw(*args,**opts):
    s=opts.get('separator',' ').join([str(x) for x in args])
    f=opts.get('pymode',False)
    if f:
        endl=opts.get('endline','\n')
        if not s.endswith(endl):s+=endl
    curses.printw(s)
    if f:
        refresh()

def raw():
    return curses.raw()
    
def noraw():
    return curses.noraw()

def cbreak():
    return curses.cbreak()

def nocbreak():
    return curses.nocbreak()

def echo():
    return curses.echo()

def noecho():
    return curses.noecho()

def prompt(prompt='Press any key to continue...'):
    '''
    Nonstandard curses function. Similar to raw_input() but only reads a single
    key.
    '''
    printw('\n'+prompt)
    getch()
    printw('\n')

def wrefresh(wstruct):
    return curses.wrefresh(wstruct)

def clear():
    return curses.clear()

def wclear(wstruct):
    return curses.wclear(wstruct)

def box(wstruct,verch,horch):
    return curses.box(wstruct,c_long(ord(verch)),c_long(ord(horch)))
    
def border(
        ls=ACS_VLINE,
        rs=ACS_VLINE,
        ts=ACS_VLINE,
        bs=ACS_VLINE,
        tl=ACS_ULCORNER,
        tr=ACS_URCORNER,
        bl=ACS_LLCORNER,
        br=ACS_LRCORNER
        ):
    ls=ord(ls) if type(ls)==type(" ") else ls
    rs=ord(rs) if type(rs)==type(" ") else rs
    ts=ord(ts) if type(ts)==type(" ") else ts
    bs=ord(bs) if type(bs)==type(" ") else bs
    tl=ord(tl) if type(tl)==type(" ") else tl
    tr=ord(tr) if type(tr)==type(" ") else tr
    bl=ord(bl) if type(bl)==type(" ") else bl
    br=ord(br) if type(br)==type(" ") else br
    return curses.border(c_uint(ls),c_uint(rs),c_uint(ts),c_uint(bs),
        c_uint(tl),c_uint(tr),c_uint(bl),c_uint(br))

def wborder(
        wstruct,
        ls=ACS_VLINE,
        rs=ACS_VLINE,
        ts=ACS_VLINE,
        bs=ACS_VLINE,
        tl=ACS_ULCORNER,
        tr=ACS_URCORNER,
        bl=ACS_LLCORNER,
        br=ACS_LRCORNER
        ):
    ls=ACS_VLINE if ls==0 else ls
    rs=ACS_VLINE if rs==0 else ls
    ts=ACS_VLINE if ts==0 else ls
    bs=ACS_VLINE if bs==0 else ls
    tl=ACS_ULCORNER if tl==0 else ls
    tr=ACS_URCORNER if tr==0 else ls
    bl=ACS_LLCORNER if bl==0 else ls
    br=ACS_LRCORNER if br==0 else ls
    ls=ord(ls) if type(ls)==type(" ") else ls
    rs=ord(rs) if type(rs)==type(" ") else rs
    ts=ord(ts) if type(ts)==type(" ") else ts
    bs=ord(bs) if type(bs)==type(" ") else bs
    tl=ord(tl) if type(tl)==type(" ") else tl
    tr=ord(tr) if type(tr)==type(" ") else tr
    bl=ord(bl) if type(bl)==type(" ") else bl
    br=ord(br) if type(br)==type(" ") else br
    return curses.wborder(wstruct,c_uint(ls),c_uint(rs),c_uint(ts),c_uint(bs),
        c_uint(tl),c_uint(tr),c_uint(bl),c_uint(br))

def has_colors():
    if curses.has_colors():
        return True
    return False

def start_color():
    return curses.start_color()
    
def init_pair(pair,f,b):
    '''
    The curses init_pair function, which defines what colors a color pair is.
    Arguments:
        pair:Color pair.
        f:Foreground color.
        b:Background color.
    '''
    if type(f)==type(c_long(0)):
        f=f.value
    if type(b)==type(c_long(0)):
        b=b.value
    return curses.init_pair(c_short(pair),c_short(f),c_short(b))

def color_pair(n):
    return (n << PDC_COLOR_SHIFT & A_COLOR)
    
def pair_number(n):
    return (n & A_COLOR) >> PDC_COLOR_SHIFT
    
def curs_set(visibility):
    '''
    Visibility should be 0,1,or 2.
    0-No cursor.
    1-Cursor.
    2-High visibility.
    Raises curses.error if it can't set it to what you want.
    '''
    r=curses.curs_set(visibility)
    if r==ERR:
        raise error('Cannot set cursor.',None)
    
def def_prog_mode():
    return curses.def_prog_mode()

def reset_prog_mode():
    return curses.reset_prog_mode()

def def_shell_mode():
    return curses.def_shell_mode()
    
def reset_shell_mode():
    return curses.reset_shell_mode()

def delay_output(ms):
    return curses.delay_output(ms)

def doupdate():
    return curses.doupdate()

def erasechar():
    return chr(curses.erasechar())

def filter():
    raise curses.error('Filter not supported in PDCurses.',None)

def flash():
    return curses.flash()

def flushinp():
    return curses.flushinp()

def isendwin():
    return bool(curses.isendwin())

def keyname(k):
    return c_char_p(curses.keyname(k)).value

def killchar():
    return chr(curses.killchr())

def longname():
    return c_char_p(curses.longname()).value

def meta(yes):
    return curses.meta(c_bool(yes))

def napms(ms):
    return curses.napms(ms)

def newpad(nlines,ncols):
    return Pad(nlines,ncols)

def nl():
    return curses.nl()

def nonl():
    return curses.nonl()

def noqiflush():
    '''
    This does nothing in PDCurses.
    '''
    return OK

def qiflush():
    '''
    This does nothing in PDcurses.
    '''
    return OK

def pair_content(pair_number):
    f=c_short()
    b=c_short()
    p=c_short(pair_number)
    curses.pair_content(p,byref(f),byref(b))
    return (f.value,b.value)

class WinDisplay():
	def __init__(self):
		raw_input()
		initscr()
		wy, wx = stdscr.getmaxyx()
		if(wy < 25 or wx < 80):
			printw('Screen size insufficient, needs 80x25.', pymode=True)
			endwin()
			exit(1)
		
		self.window = Window(25,80)
		#self.window.auto_refresh(True)
	
	def write_string(self, x, y, str):
		self.window.addstr(y, x, str)
	
	def write_char(self, x, y, char):
		self.window.addch(y, x, char)
	
	def killme(self):
		endwin()
		
	def getstr(self):
		return self.window.getstr()
		
	def getch(self):
		return self.window.getch()

	def refresh(self):
		return self.window.refresh()
	
